﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace PLCTools.Driver
{
    public class TestSiemensDriver : PLCDriver
    {
        private VSiemensDriver _driver = null;
        public override string ToString()
        {
            return "TestSiemens";
        }
        public override bool Connect(string ip, string param)
        {
            lock (this)
            {
                if (IsConnected)
                {
                    return true;
                }
                try
                {
                    _driver = new VSiemensDriver(ip, param);
                    _driver.Connect();
                }
                catch
                {
                }
                IsConnected = _driver.IsConnected;
                return IsConnected;
            }
        }

        public override void Disconnect()
        {
            lock (this)
            {
                try
                {
                    IsConnected = false;
                    _driver.Disconnect();
                }
                catch
                {
                }
            }
        }

        public override bool ReadBytes(object addr, object[] data, DataType type, out byte[] buf)
        {
            buf = null;
            if (!IsConnected)
            {
                return false;
            }
            try
            {
                int len = GetLenFromDataType(type);
                buf = new byte[len * data.Length];
                int pos = 0;
                string[] address = addr.ToString().Split(new char[] { '.' });
                if (address.Length > 1)
                {
                    pos = Convert.ToInt32(address[1]);
                }
                if (type == DataType.SIE_BOOL)
                {
                    if (Convert.ToUInt16(addr.ToString().Substring(addr.ToString().Length - 1)) >= 8)
                    {
                        return false;
                    }
                    pos = pos * 8;
                    if (address.Length > 2)
                    {
                        pos += Convert.ToInt32(address[2]);
                    }
                    bool val;
                    for (int i = 0; i < data.Length; i++)
                    {
                        if (!_driver.ReadDBBit(Convert.ToInt32(address[0]), pos + i, out val))
                        {
                            return false;
                        }
                        data[i] = val;
                        buf[i] = Convert.ToByte(val);
                    }
                }
                else
                {
                    if (!_driver.ReadDBBytes(Convert.ToInt32(address[0]), pos, buf))
                    {
                        return false;
                    }
                    byte[] temp;
                    for (int i = 0; i < data.Length; i++)
                    {
                        temp = new byte[len];
                        Array.Copy(buf, i * len, temp, 0, temp.Length);
                        data[i] = GetObjectFromBytes(type, temp);
                    }
                }
                return true;
            }
            catch
            {
            }
            return false;
        }

        public override bool WriteBytes(object addr, object[] data, DataType type)
        {
            if (!IsConnected)
            {
                return false;
            }

            try
            {
                int len = GetLenFromDataType(type);
                byte[] buf = new byte[len * data.Length];
                byte[] temp;
                for (int i = 0; i < data.Length; i++)
                {
                    temp = GetBytesFromDataType(type, data[i]);
                    Array.Copy(temp, 0, buf, i * len, temp.Length);
                }
                int pos = 0;
                string[] address = addr.ToString().Split(new char[] { '.' });
                if (address.Length > 1)
                {
                    pos = Convert.ToInt32(address[1]);
                }
                if (type == DataType.SIE_BOOL)
                {
                    pos = pos * 8;
                    if (address.Length > 2)
                    {
                        pos += Convert.ToInt32(address[2]);
                    }
                    for (int i = 0; i < data.Length; i++)
                    {
                        if (!_driver.WriteDBBit(Convert.ToInt32(address[0]), pos + i, Convert.ToBoolean(data[i])))
                        {
                            return false;
                        }
                    }
                }
                else
                {
                    if (!_driver.WriteDBBytes(Convert.ToInt32(address[0]), pos, buf.Length, buf))
                    {
                        return false;
                    }
                }
                return true;
            }
            catch
            {
            }
            return false;
        }

        public override bool ReadBytesPLCDB(int DBNumber, int start, byte[] buf)
        {
            bool bRes = _driver.ReadDBBytes(DBNumber, start, buf);
            IsConnected = _driver.IsConnected;
            return bRes;
        }
        public override bool WriteBytesPLCDB(int DBNumber, int start, byte[] buf)
        {
            bool bRes = _driver.WriteDBBytes(DBNumber, start, buf.Length, buf);
            IsConnected = _driver.IsConnected;
            return bRes;
        }

        public override bool WriteBitPLCDB(int DBNumber, int pos, bool val)
        {
            bool bRes = _driver.WriteDBBit(DBNumber, pos, val);
            IsConnected = _driver.IsConnected;
            return bRes;
        }

    }

    public class VSiemensDriver
    {
        private object syncLock = new object();
        private TcpClient _client;
        private StreamReader _reader = null;
        private StreamWriter _writer = null;
        private string _addr;
        private int _port = 102;

        private bool _isConnected = false;
        public bool IsConnected { get { return _isConnected; } }

        public VSiemensDriver(string addr, string port)
        {
            _addr = addr;
            if (port.Trim() != "")
            {
                _port = Convert.ToInt32(port);
            }
        }

        public void Connect()
        {
            try
            {
                lock (syncLock)
                {
                    if (_isConnected)
                        return;
                    //if (logger.IsDebugEnabled)
                    //{
                    //    logger.Debug(string.Format("Connect with libnodave: name={0}; IP={1}; port={2}", _Cfg.Name, _addr, _port));
                    //}
                    _client = new TcpClient();
                    _client.Connect(IPAddress.Parse(_addr), _port);
                    NetworkStream ns = _client.GetStream();
                    _reader = new StreamReader(ns);
                    _writer = new StreamWriter(ns);

                    _isConnected = true;
                }
            }
            catch
            {
            }
        }

        public void Disconnect()
        {
            try
            {
                lock (syncLock)
                {
                    if (IsConnected)
                    {
                        _isConnected = false;
                        _reader.Close();
                        _writer.Close();
                        _client.Close();
                        //if (logger.IsDebugEnabled)
                        //{
                        //    logger.Debug(string.Format("Disconnect with libnodave: name={0}", _Cfg.Name));
                        //}
                        //SetConnected(false);
                    }
                }
            }
            catch { }
        }

        public bool ReadDBBit(int DBNumber, int pos, out bool val)
        {
            val = false;
            if (!IsConnected)
            {
                Connect();
                if (!IsConnected) return false;
            }
            byte[] buf = new byte[1];

            int rs = 0;
            lock (syncLock)
            {
                try
                {
                    _writer.WriteLine((int)VECommand.ReadDBBit + ";" + DBNumber + ";" + pos);
                    _writer.Flush();
                    string result = _reader.ReadLine();
                    if (result == null || result == "")
                    {
                        throw new Exception();
                    }
                    try
                    {
                        string[] ary = result.Split(new char[] { ',' });
                        rs = Convert.ToInt32(ary[0]);
                        val = ary[1] == "1" ? true : false;
                    }
                    catch
                    {
                    }
                }
                catch
                {
                    Disconnect();
                    rs = 1;
                    throw new Exception();
                }

                //if (logger.IsDebugEnabled)
                //{
                //    logger.Debug(string.Format("ReadBit: result={0}, DBNumber={1}, pos={2}, val={3}", rs, DBNumber, pos, val));
                //}
            }
            return 0 == rs;
        }

        public bool ReadDBBytes(int DBNumber, int start, byte[] buf)
        {
            if (!IsConnected)
            {
                Connect();
                if (!IsConnected) return false;
            }
            int rs = 0;
            lock (syncLock)
            {
                try
                {
                    _writer.WriteLine((int)VECommand.ReadDBBytes + ";" + DBNumber + ";" + start + ";" + buf.Length);
                    _writer.Flush();
                    string result = _reader.ReadLine();
                    if (result == null || result == "")
                    {
                        throw new Exception();
                    }
                    try
                    {
                        string[] ary = result.Split(new char[] { ',' });
                        rs = Convert.ToInt32(ary[0]);
                        byte[] buffer = ByteUtility.ConvertHexStrToBytes(ary[1]);
                        Array.Copy(buffer, buf, buffer.Length);
                    }
                    catch
                    {
                    }
                }
                catch
                {
                    Disconnect();
                    rs = 1;
                    throw new Exception();
                }
            }
            return 0 == rs;
        }

        public bool WriteDBBit(int DBNumber, int pos, bool val)
        {
            if (!IsConnected)
            {
                Connect();
                if (!IsConnected) return false;
            }
            byte[] buf = new byte[] { (byte)(val ? 1 : 0) };
            int rs = 0;
            lock (syncLock)
            {
                try
                {
                    _writer.WriteLine((int)VECommand.WriteDBBit + ";" + DBNumber + ";" + pos + ";" + (val ? 1 : 0));
                    _writer.Flush();
                    string result = _reader.ReadLine();
                    if (result == null || result == "")
                    {
                        throw new Exception();
                    }
                    try
                    {
                        string[] ary = result.Split(new char[] { ',' });
                        rs = Convert.ToInt32(ary[0]);
                    }
                    catch
                    {
                    }
                }
                catch
                {
                    Disconnect();
                    rs = 1;
                }

                //if (logger.IsDebugEnabled)
                //{
                //    logger.Debug(string.Format("WriteBit: result={0}, DBNumber={1}, pos={2}, val={3}", rs, DBNumber, pos, val));
                //}
            }
            return 0 == rs;
        }

        public bool WriteDBBytes(int DBNumber, int start, int len, byte[] buf)
        {
            if (!IsConnected)
            {
                Connect();
                if (!IsConnected) return false;
            }
            int rs = 0;
            lock (syncLock)
            {
                try
                {
                    _writer.WriteLine((int)VECommand.WriteDBBytes + ";" + DBNumber + ";" + start + ";" +
                        ByteUtility.ConvertBytesToHexStr(buf, len));
                    _writer.Flush();
                    string result = _reader.ReadLine();
                    if (result == null || result == "")
                    {
                        throw new Exception();
                    }
                    rs = Convert.ToInt32(result);
                }
                catch
                {
                    Disconnect();
                    rs = 1;
                }
            }
            return 0 == rs;
        }

        public bool WriteDBBytes(int DBNumber, int start, byte[] buf)
        {
            return WriteDBBytes(DBNumber, start, buf.Length, buf);
        }

       
    }
}
